// Debug.cpp
 
// Implementacia uzitocnych debuggovacich funkcii

#include "Common/Support/Debug.h"

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
#include <string.h>

#ifdef DEBUG
// Pocas kompilacie s Visual C++, treba obe makra DEBUG aj _DEBUG
#if defined(_MSC_VER) && !defined(_DEBUG)
#error To build debug version with Visual C++, define both DEBUG and _DEBUG
#endif // defined(_MSC_VER) && !defined(_DEBUG)


#ifdef _MSC_VER

#include <crtdbg.h>
#include <share.h>
#include <direct.h>

#else // _MSC_VER

#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

#endif // _MSC_VER

// Adresar pre debugging
#define DEBUG_DIR "Debug"


static char fileName[128] = "";
static char nameBase[128] = "";
static char playerId[64] = "";
static int tag = 1;
static FILE* file = NULL;


// Pomocna funkcia, ktora zapise udaje do debuggovacieho suboru
static void debug(const char* pre, const char* format, const char* post, va_list args)
{
	if(file == NULL)
	{
		struct tm* t;		
		int prevSec = -1;

		// Vytvor adresar "Debug", ak este neexistuje
#ifdef _MSC_VER
		(void) _mkdir(DEBUG_DIR);
#else // _MSC_VER
		(void) mkdir(DEBUG_DIR, 0755);
#endif // _MSC_VER

		// Otvor subor max. 50x (2 timy po 11 hracov + koucovia + ...)
		for(int i = 1; i <= 50; i++)
		{
			// Zachyt miestny cas
			time_t tt;
			time(&tt);
			t = localtime(&tt);

			// Lisi sa miestny cas od posledneho casu
			if(t->tm_sec == prevSec)
			{
				tag++;
			}
			else
			{
				tag = 1;
				prevSec = t->tm_sec;
			}

			// Generuje meno suboru
			sprintf(fileName, DEBUG_DIR "/%02d%02d%02d_%02d%02d%02d_%s%02d.txt", 
				t->tm_year % 100, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, playerId, tag);

#ifdef _MSC_VER
			// Otvori a uzamkne subor
			file = _fsopen(fileName, "a+", _SH_DENYWR);
			if(file != NULL)	// otvoreny a zamknuty
				break;			
#else // _MSC_VER
			// Otvori subor
			file = fopen(fileName, "a+");
			if(file == NULL)
				continue;		// Subor nemohol byt otvoreny, vyskusa dalsi subor

			// Snazi sa ho uzamknut
			struct flock l;
			l.l_type = F_WRLCK; l.l_whence = 0; l.l_start = 0; l.l_len = 0;
			if(fcntl(fileno(file), F_SETLK, &l) == 0) 
				break;	// otvoreny a zamknuty

			// Nie je zamknuty
			fclose(file); file = NULL;
#endif // _MSC_VER
		}

		// Uspech?
		if(file == NULL)
		{
			fprintf(stderr, "Chyba pri otvarani debuggovacieho suboru!\n");
			abort();
		}

		sprintf(nameBase, DEBUG_DIR "/%02d%02d%02d_%02d%02d%02d_", 
			t->tm_year % 100, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);

		fprintf(stderr, "Debuggovaci subor je %s\n", fileName);
	}

	fprintf(file, "%s", pre);
	vfprintf(file, format, args);
	fprintf(file, "%s", post);
	
	fflush(file);
}

void DBG_SET_ID(const char* id)
{
	sprintf(playerId, "%s_", id);

	if(file != NULL)
	{
		// Premenuje debuggovaci subor 
		char newName[128];
		sprintf(newName, "%s%s_%02d.txt", nameBase, id, tag);
		FILE* tmp = fopen(newName, "w+");
		if(tmp == NULL)
		{
			fprintf(stderr, "Chyba otvorenia suboru pri premenovani debuggovacieho suboru na %s\n", newName);
			return;
		}

		if(fseek(file, 0, SEEK_SET) != 0)
		{
			fprintf(stderr, "Vyhladavacia chyba pri premenovani debuggovacieho suboru na %s\n", newName);
			fclose(tmp); return;
		}
		
		size_t bytes;
		char buf[4096];
		while(!feof(file))
		{
			bytes = fread((void*) buf, 1, 4096, file);
			if(bytes != 4096 && ferror(file))
			{
				fprintf(stderr, "Citacia chyba pri premenovani debuggovacieho suboru na %s\n", newName);
				fclose(tmp); return;
			}

			bytes = fwrite((void*) buf, 1, bytes, tmp);
			if(bytes != 4096 && ferror(tmp))
			{
				fprintf(stderr, "Zapisovacia chyba pri premenovani debuggovacieho suboru na %s\n", newName);
				fclose(tmp); return;
			}
		}

		fclose(file);
		file = tmp;

#ifdef _MSC_VER
		_unlink(fileName);
#else // _MSC_VER
		unlink(fileName);
#endif // _MSC_VER

		strcpy(fileName, newName);
		fprintf(stderr, "Debuggovaci subor sa zmenil na %s\n", fileName);
	}
}

void DBG(const char* format, ...)
{
	va_list args;
	va_start(args, format);
	debug("", format, "", args);
	printf("[DBG] ");
	vprintf(format, args);
	va_end(args);
}

void DBGN(const char* format, ...)
{
	va_list args;
	va_start(args, format);
	debug("", format, "\n", args);
	printf("[DBG] ");
	vprintf(format, args);
	printf("\n");
	va_end(args);
}

#ifdef _MSC_VER
static int msvcReportHook(int type, char* message, int* returnValue)
{
	int len = strlen(message);
	bool endLine = (len > 0 && message[len - 1] == '\n');

	switch(type)
	{
	case _CRT_ASSERT:
//		DBG("[CRT A] %s%s", message, endLine ? "" : "\n");
		break;
	case _CRT_WARN:
//		DBG("[CRT W] %s%s", message, endLine ? "" : "\n");
		break;
	case _CRT_ERROR:
//		DBG("[CRT E] %s%s", message, endLine ? "" : "\n");
		break;
	}

	return 1;
}
#endif // _MSC_VER

void DBG_INIT()
{
#ifdef _MSC_VER
	// Everything should go to debug file
	_CrtSetReportHook(msvcReportHook);

	// Heap debugging
	int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
	_CrtSetDbgFlag(flag | _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif // _MSC_VER
}

void DBG_DONE()
{
//#ifdef _MSC_VER
	// Heap debugging
	//_CrtCheckMemory();
	//_CrtDumpMemoryLeaks();
	//_CrtMemDumpAllObjectsSince(NULL);
//#endif // _MSC_VER
}

#endif // DEBUG

void ERR(const char* format, ...)
{
	va_list args;
	va_start(args, format);
#ifdef DEBUG
	debug("[ERROR] ", format, "", args);
#endif // DEBUG
	fprintf(stderr, "[ERROR] ");
	vfprintf(stderr, format, args);
	va_end(args);
}

void ERRN(const char* format, ...)
{
	va_list args;
	va_start(args, format);
#ifdef DEBUG
	debug("[ERROR] ", format, "\n", args);
#endif // DEBUG
	fprintf(stderr, "[ERROR] ");
	vfprintf(stderr, format, args);
	fprintf(stderr, "\n");
	va_end(args);
}

void WARN(const char* format, ...)
{
	va_list args;
	va_start(args, format);
#ifdef DEBUG
	debug("[WARNING] ", format, "", args);
#endif // DEBUG
	fprintf(stderr, "[WARNING] ");
	vfprintf(stderr, format, args);
	va_end(args);
}

void WARNN(const char* format, ...)
{
	va_list args;
	va_start(args, format);
#ifdef DEBUG
	debug("[WARNING] ", format, "\n", args);
#endif // DEBUG
	fprintf(stderr, "[WARNING] ");
	vfprintf(stderr, format, args);
	fprintf(stderr, "\n");
	va_end(args);
}

void TXT(const char* format, ...)
{
	va_list args;
	va_start(args, format);
#ifdef DEBUG
	debug("", format, "", args);
#endif // DEBUG
	vfprintf(stderr, format, args);
	va_end(args);
}

void TXTN(const char* format, ...)
{
	va_list args;
	va_start(args, format);
#ifdef DEBUG
	debug("", format, "\n", args);
#endif // DEBUG
	vfprintf(stderr, format, args);
	fprintf(stderr, "\n");
	va_end(args);
}
